package cn.bugstack.mybatis.scripting.xmltags;


import cn.bugstack.mybatis.builder.BaseBuilder;
import cn.bugstack.mybatis.mapping.SqlSource;
import cn.bugstack.mybatis.scripting.defaults.RawSqlSource;
import cn.bugstack.mybatis.session.Configuration;
import org.dom4j.Element;
import org.dom4j.Node;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description 解析 XML 脚本
 */

public class XMLScriptBuilder extends BaseBuilder {

    private Element element;
    private boolean isDynamic;
    private Class<?> parameterType;

    private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();

    public XMLScriptBuilder(Configuration configuration, Element element, Class<?> parameterType) {
        super(configuration);
        this.element = element;
        this.parameterType = parameterType;
        initNodeHandlerMap();
    }

    private void initNodeHandlerMap() {
        // 9种，实现其中2种 trim/where/set/foreach/if/choose/when/otherwise/bind
        nodeHandlerMap.put("trim", new TrimHandler());
        nodeHandlerMap.put("if", new IfHandler());
    }

    public SqlSource parseScriptNode() {
        // 解析动态标签为对应的sqlNode
        List<SqlNode> contents = parseDynamicTags(element);
        // 包装为MixedSqlNode，传递给SqlSource
        MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
        // 如果含有动态标签，则返回DynamicSqlSource，否则返回RawSqlSource
        SqlSource sqlSource = null;
        if (isDynamic) {
            sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
        } else {
            sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
        }
        return sqlSource;
    }

    /**
     *     <select id="queryActivityById" parameterType="cn.bugstack.mybatis.test.po.Activity" resultMap="activityMap">
     *         SELECT activity_id, activity_name, activity_desc, create_time, update_time
     *         FROM activity
     *         <trim prefix="where" prefixOverrides="AND | OR" suffixOverrides="and">
     *             <if test="null != activityId">
     *                 activity_id = #{activityId}
     *             </if>
     *         </trim>
     *     </select>
     * @param element
     * @return
     */
    List<SqlNode> parseDynamicTags(Element element) {
        List<SqlNode> contents = new ArrayList<>();
        List<Node> content = element.content();
        for (Node node : content) {
            if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) {
                String text = node.getText();
                TextSqlNode textSqlNode = new TextSqlNode(text);
                if (textSqlNode.isDynamic()) {
                    contents.add(textSqlNode);
                    isDynamic = true;
                } else {
                    // 如果不是动态sql
                    contents.add(new StaticTextSqlNode(text));
                }
            } else if (node.getNodeType() == Node.ELEMENT_NODE) {
                String nodeName = node.getName();
                // 根据不同的node(if、trim)分别处理
                NodeHandler nodeHandler = nodeHandlerMap.get(nodeName);
                if (nodeHandler == null) {
                    throw new RuntimeException("Unknown element <" + nodeName + "> in SQL statement.");
                }
                nodeHandler.handleNode((Element) node, contents);
                isDynamic = true;
            }
        }
        // element.getText 拿到 SQL
        return contents;
    }


    private interface NodeHandler {
        void handleNode(Element nodeToHandle, List<SqlNode> targetContents);
    }

    /**
     * 处理 TrimSqlNode
     */
    private class TrimHandler implements NodeHandler {
        @Override
        public void handleNode(Element nodeToHandle, List<SqlNode> targetContents) {
            List<SqlNode> contents = parseDynamicTags(nodeToHandle);
            MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
            String prefix = nodeToHandle.attributeValue("prefix");
            String prefixOverrides = nodeToHandle.attributeValue("prefixOverrides");
            String suffix = nodeToHandle.attributeValue("suffix");
            String suffixOverrides = nodeToHandle.attributeValue("suffixOverrides");
            TrimSqlNode trimSqlNode = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
            targetContents.add(trimSqlNode);
        }
    }

    /**
     * 处理 IfSqlNode
     */
    private class IfHandler implements NodeHandler {
        @Override
        public void handleNode(Element nodeToHandle, List<SqlNode> targetContents) {
            List<SqlNode> contents = parseDynamicTags(nodeToHandle);
            MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
            String test = nodeToHandle.attributeValue("test");
            IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
            targetContents.add(ifSqlNode);
        }
    }

}
